﻿#include "openglwidget.h"
#include <QSurfaceFormat>
#include <QDebug>
#include <StlImporter.h>
#include "Vector3D.h"
#include <QtGlobal>

OpenGLWidget::OpenGLWidget(QWidget *parent)
    : QOpenGLWidget(parent), m_VBO(QOpenGLBuffer::VertexBuffer), m_EBO(QOpenGLBuffer::IndexBuffer)
{
    // 设置请求的表面格式
    QSurfaceFormat format;
    format.setAlphaBufferSize(24);
    format.setVersion(3,3);
    format.setSamples(10);//设置重采样次数，用于反走样
    this->setFormat(format);
#ifdef Q_OS_WIN
    renderModel("E:\\QtOpenGL\\Flange.stl");
#else
    renderModel("/home/chan/data/RenderWithOpenGL/model/Flange.stl");
#endif
}

void OpenGLWidget::renderModel(QString path)
{
    m_Vertices.clear();
    m_Indices.clear();
    m_Normal.clear();
    m_Position.clear();
    // 加载模型
    auto loader = new StlImporter;
    if(loader->loadStl(path.toStdString())){
        auto vs = loader->getVertices();
        auto ns = loader->getNormals();

        auto numOfVertices = vs.size();
        auto numOfNormal = ns.size();

        if(numOfNormal == numOfVertices){
            for (unsigned long index = 0; index < numOfVertices; ++index) {
                m_Vertices.append({vs.at(index).x(), vs.at(index).y(), vs.at(index).z()});
                m_Vertices.append({ns.at(index).x(), ns.at(index).y(), ns.at(index).z()});
            }
        }else{
            for (unsigned long index = 0; index < numOfVertices; ++index) {
                m_Vertices.append({vs.at(index).x(), vs.at(index).y(), vs.at(index).z()});
                m_Vertices.append({1.0, 1.0, 1.0});
            }
        }

        auto is = loader->getIndices();
        for (auto i : is) {
            m_Indices.push_back(i);
        }
    }else {
        qDebug() << "Failed to load stl file!";
    }
}
OpenGLWidget::~OpenGLWidget()
{
    makeCurrent();
    m_Shaderprogram.release();
    m_VAO.destroy();
    m_VBO.destroy();
    doneCurrent();
}


void OpenGLWidget::setWirefame(bool checked)
{
    makeCurrent();
    if(checked){
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    }else {
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    }
    update();
    doneCurrent();
}

void OpenGLWidget::setProjection(bool checked)
{
    makeCurrent();
    m_IsCheckedProjection = !checked;
    update();
    doneCurrent();
}

void OpenGLWidget::initializeGL()
{
    // 初始化OpenGL函数（指针）
    this->initializeOpenGLFunctions();

    // 创建着色器程序
    m_Shaderprogram.create();

    // 设置顶点着色器源码
    if(!m_Shaderprogram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/shader/shader.vert")){
        qDebug() << "ERROR:" << m_Shaderprogram.log();
    }

    // 设置片段着色器源码
    if(!m_Shaderprogram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/shader/shader.frag")){
        qDebug() << "ERROR:" << m_Shaderprogram.log();
    }

    // 链接着色器程序
    if(!m_Shaderprogram.link()){
        qDebug() << "ERROR:" << m_Shaderprogram.log();
    }

    // 创建VAO，VBO，并绑定
    m_VAO.create();
    m_VAO.bind();
    m_VBO.create();
    m_VBO.bind();
    m_EBO.create();

    //m_VBO.setUsagePattern(QOpenGLBuffer::StaticDraw);
    m_VBO.allocate(m_Vertices.data(), sizeof(GLfloat) * m_Vertices.size());

    // 打卡属性并告知显卡如何解系缓存里的属性值
    m_Shaderprogram.setAttributeBuffer("aPos", GL_FLOAT, 0, 3, sizeof(GLfloat) * 6);
    m_Shaderprogram.enableAttributeArray("aPos");

    m_Shaderprogram.setAttributeBuffer("aNormal", GL_FLOAT, sizeof(GLfloat) * 3, 3, sizeof(GLfloat) * 6);
    m_Shaderprogram.enableAttributeArray("aNormal");

    m_EBO.bind();
    //m_EBO.setUsagePattern(QOpenGLBuffer::StaticDraw);
    m_EBO.allocate(m_Indices.data(), sizeof(GLint) * m_Indices.size());
    //m_VAO.release();
    // 开启深度测试
    this->glEnable(GL_DEPTH_TEST);

    QVector3D eye(0, 0, 100), target(0, 0,0), up(0,1,0);
    m_View.lookAt(eye, target, up);
}

void OpenGLWidget::resizeGL(int width, int height)
{
    glViewport(0, 0, width, height);
    if(m_IsCheckedProjection){
        m_Projection.setToIdentity();
        m_Projection.perspective(60.0f, (GLfloat)width / (GLfloat)height, 0.001f, 10000.0f);//视角-宽高比例-zNear-zFar
    }else{
        m_Projection.setToIdentity();
        m_Projection.ortho(-0.5 * width, 0.5 * width, -0.5 * height, 0.5 * height, 0.001f, 10000.0f);
        //m_Projection.frustum();
    }
}

void OpenGLWidget::paintGL()
{
    // 清除颜色缓存和深度缓存
    this->glClearColor(0.0f,0.333f,1.0f,1.0f);
    this->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // 绑定着色器程序
    m_Shaderprogram.bind();

    // 定义光源位置、颜色和模型颜色
    QVector3D lightPos(0.0f,0.0f,50.0f);
    QVector3D lightColor(1.0f,1.0f,1.0f);
    QVector3D modelColor(1.0f,1.0f,0.88f);

    // 通过uniform传递给片段着色器
    m_Shaderprogram.setUniformValue("lightPos", lightPos);
    m_Shaderprogram.setUniformValue("lightColor", lightColor);
    m_Shaderprogram.setUniformValue("modelColor", modelColor);

    m_Model.setToIdentity();
    m_Model.translate(m_XTrans, m_YTrans, m_ZTrans);
    m_Model.rotate(m_Rotation);

    // 设置摄像机参数
    m_Shaderprogram.setUniformValue("view", m_View);
    //qDebug() << m_View;
    m_Shaderprogram.setUniformValue("projection", m_Projection);
    qDebug() << m_Projection;
    m_Shaderprogram.setUniformValue("model", m_Model);

    QOpenGLVertexArrayObject::Binder bind(& m_VAO);//绑定VAO
    //m_VAO.bind();
    // 使用VBO绘制
    //this->glDrawArrays(GL_TRIANGLES, 0, m_Vertices.size() / sizeof(float));
    // 使用EBO绘制
    this->glDrawElements(GL_TRIANGLES, m_Indices.size(), GL_UNSIGNED_INT, 0);
}

void OpenGLWidget::mousePressEvent(QMouseEvent *event)
{
    m_MousePos = QVector2D(event->pos());
    event->accept();
}

void OpenGLWidget::mouseMoveEvent(QMouseEvent *event)
{
    if(event->buttons() == Qt::LeftButton){
        QVector2D newPos = (QVector2D)event->pos();
        QVector2D diff = newPos - m_MousePos;
        qreal angle = diff.length() / 3.6;

        QVector3D rotationAxis = QVector3D(diff.y(), diff.x(), 0.0).normalized();
        m_Rotation = QQuaternion::fromAxisAndAngle(rotationAxis, angle) * m_Rotation;
        m_MousePos = newPos;
        this->update();
    }
    event->accept();
}

void OpenGLWidget::wheelEvent(QWheelEvent *event)
{
    QPoint numDegrees = event->angleDelta() / 8;

    if(numDegrees.y() > 0){
        m_ZTrans += 1.0f;
    }
    else if(numDegrees.y() < 0){
        m_ZTrans -= 1.0f;
    }
    this->update();
    event->accept();
}
